package code.zzl.monitor.admin.infrastructure.repository;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.DbRuntimeException;
import code.zzl.monitor.admin.domain.model.entity.MonitorDataEntity;
import code.zzl.monitor.admin.domain.model.entity.MonitorDataMapEntity;
import code.zzl.monitor.admin.domain.model.entity.MonitorFlowDesignerEntity;
import code.zzl.monitor.admin.domain.model.valobj.GatherNodeExpressionVO;
import code.zzl.monitor.admin.domain.repository.IMonitorRepository;
import code.zzl.monitor.admin.domain.model.valobj.MonitorTreeConfigVO;
import code.zzl.monitor.admin.infrastructure.po.*;
import code.zzl.monitor.admin.infrastructure.redis.IRedisService;
import code.zzl.monitor.admin.infrastructure.repository.service.*;
import code.zzl.monitor.constant.RedissonConstant;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @ Author 龙
 * @ Date 2024-07-31 22:43
 * @ Description: TODO
 */
@Repository
public class MonitorRepository implements IMonitorRepository {
    @Resource
    private IMonitorMapNodeService monitorMapNodeService;
    @Resource
    private IMonitorMapNodeLinkService monitorMapNodeLinkService;
    @Resource
    private IMonitorMapNodeFieldService monitorMapNodeFieldService;
    @Resource
    private IMonitorMapService monitorMapService;
    @Resource
    private IRedisService redisService;
    @Resource
    private IMonitorService monitorService;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Override
    public MonitorTreeConfigVO queryMonitorFlowData(String monitorId) {
        // 监控节点配置
        List<MonitorDataMapNode> monitorDataMapNodes = monitorMapNodeService.lambdaQuery()
                .eq(MonitorDataMapNode::getMonitorId, monitorId).list();

        List<MonitorTreeConfigVO.Node> nodeList = new ArrayList<>();
        for (MonitorDataMapNode monitorDataMapNode : monitorDataMapNodes) {
            // 查询缓存节点流量值
            String cacheKey = RedissonConstant.MONITOR_NODE_DATA_COUNT_KEY + monitorId +
                    RedissonConstant.UNDERLINE + monitorDataMapNode.getMonitorNodeId();
            Long count = redisService.getAtomicLong(cacheKey);
            nodeList.add(MonitorTreeConfigVO.Node.builder()//将monitorDataMapNode对象转换为MonitorTreeConfigVO对象
                    .monitorNodeId(monitorDataMapNode.getMonitorNodeId())
                    .monitorNodeName(monitorDataMapNode.getMonitorNodeName())
                    .loc(monitorDataMapNode.getLoc())
                    .color(monitorDataMapNode.getColor())
                    .monitorNodeValue(ObjectUtil.isNull(count) ? "0" : String.valueOf(count))
                    .build());
        }

        // 监控节点链路
        List<MonitorDataMapNodeLink> monitorDataMapNodeLinks = monitorMapNodeLinkService.lambdaQuery()
                .eq(MonitorDataMapNodeLink::getMonitorId, monitorId).list();

        //将链路monitorDataMapNodeLinks集合对象以fromId分组，对应地以toId提取出来，再收集起来
        Map<String, List<String>> fromMonitorNodeIdToNodeIds = monitorDataMapNodeLinks
                .stream()
                .collect(Collectors.groupingBy(MonitorDataMapNodeLink::getFromMonitorNodeId,
                        Collectors.mapping(MonitorDataMapNodeLink::getToMonitorNodeId,
                                Collectors.toList())));
        List<MonitorTreeConfigVO.Link> linkList = new ArrayList<>();
        for (MonitorDataMapNodeLink monitorDataMapNodeLink : monitorDataMapNodeLinks) {
            // 获取节点值
            String fromCacheKey = RedissonConstant.MONITOR_NODE_DATA_COUNT_KEY + monitorId +
                    RedissonConstant.UNDERLINE + monitorDataMapNodeLink.getFromMonitorNodeId();
            Long fromCacheCount = redisService.getAtomicLong(fromCacheKey);
            Long toCacheCount = 0L;

            // 合并所有值
            //取出同一个起点的集合
            List<String> toNodeIds = fromMonitorNodeIdToNodeIds.get(monitorDataMapNodeLink.getFromMonitorNodeId());
            for (String toNodeId : toNodeIds) {
                String toCacheKey = RedissonConstant.MONITOR_NODE_DATA_COUNT_KEY + monitorId + RedissonConstant.UNDERLINE + toNodeId;
                toCacheCount += redisService.getAtomicLong(toCacheKey);
            }

            long differenceValue = (null == fromCacheCount ? 0L : fromCacheCount) - toCacheCount;

            //将MonitorDataMapNodeLink对象转换为MonitorTreeConfigVO.Link对象
            linkList.add(MonitorTreeConfigVO.Link.builder()
                    .fromMonitorNodeId(monitorDataMapNodeLink.getFromMonitorNodeId())
                    .toMonitorNodeId(monitorDataMapNodeLink.getToMonitorNodeId())
                    .linkKey(String.valueOf(monitorDataMapNodeLink.getId()))
                    .linkValue(String.valueOf(differenceValue <= 0 ? 0 : differenceValue))
                    .build());
        }
        return MonitorTreeConfigVO.builder()
                .monitorId(monitorId)
                .nodeList(nodeList)
                .linkList(linkList)
                .build();
    }

    @Override
    public List<MonitorDataMapEntity> queryMonitorDataMapEntityList() {
        List<MonitorDataMap> monitorDataMapList = monitorMapService.list();
        List<MonitorDataMapEntity> monitorDataMapEntities = new ArrayList<>();
        for (MonitorDataMap monitorDataMap : monitorDataMapList) {
            monitorDataMapEntities.add(MonitorDataMapEntity.builder()
                    .monitorId(monitorDataMap.getMonitorId())
                    .monitorName(monitorDataMap.getMonitorName())
                    .build());
        }
        return monitorDataMapEntities;
    }

    @Override
    public List<MonitorDataEntity> queryMonitorDataEntityList(MonitorDataEntity monitorDataEntity) {
        MonitorData monitorDataReq = new MonitorData();
        monitorDataReq.setMonitorId(monitorDataEntity.getMonitorId());
        monitorDataReq.setMonitorName(monitorDataEntity.getMonitorName());
        monitorDataReq.setMonitorNodeId(monitorDataEntity.getMonitorNodeId());

        List<MonitorData> monitorDataList = monitorService.lambdaQuery().setEntity(monitorDataReq).list();
        List<MonitorDataEntity> monitorDataEntities = new ArrayList<>();
        for (MonitorData monitorData : monitorDataList) {
            MonitorDataEntity monitorDataEntityRes = new MonitorDataEntity();
            monitorDataEntityRes.setMonitorId(monitorData.getMonitorId());
            monitorDataEntityRes.setMonitorName(monitorData.getMonitorName());
            monitorDataEntityRes.setMonitorNodeId(monitorData.getMonitorNodeId());
            monitorDataEntityRes.setSystemName(monitorData.getSystemName());
            monitorDataEntityRes.setClazzName(monitorData.getClazzName());
            monitorDataEntityRes.setMethodName(monitorData.getMethodName());
            monitorDataEntityRes.setAttributeName(monitorData.getAttributeName());
            monitorDataEntityRes.setAttributeField(monitorData.getAttributeField());
            monitorDataEntityRes.setAttributeValue(monitorData.getAttributeValue());
            monitorDataEntities.add(monitorDataEntityRes);
        }
        return monitorDataEntities;

    }

    @Override
    public void updateMonitorFlowDesigner(MonitorFlowDesignerEntity monitorFlowDesignerEntity) {
        transactionTemplate.execute(status -> {
            try {
                List<MonitorFlowDesignerEntity.Node> nodeList = monitorFlowDesignerEntity.getNodeList();
                for (MonitorFlowDesignerEntity.Node node : nodeList) {
                    boolean update = monitorMapNodeService.lambdaUpdate().set(MonitorDataMapNode::getLoc, node.getLoc())
                            .eq(MonitorDataMapNode::getMonitorId, monitorFlowDesignerEntity.getMonitorId())
                            .eq(MonitorDataMapNode::getMonitorNodeId, node.getMonitorNodeId()).update();
                    if (!update){
                        throw new DbRuntimeException("更新失败");
                    }
                }

                List<MonitorFlowDesignerEntity.Link> linkList = monitorFlowDesignerEntity.getLinkList();
                boolean remove = monitorMapNodeLinkService.remove(monitorMapNodeLinkService.lambdaQuery().eq
                        (MonitorDataMapNodeLink::getMonitorId, monitorFlowDesignerEntity.getMonitorId()));

                List<MonitorDataMapNodeLink> monitorDataMapNodeLinks = new ArrayList<>();
                for (MonitorFlowDesignerEntity.Link link : linkList) {
                    MonitorDataMapNodeLink monitorDataMapNodeLinkReq = new MonitorDataMapNodeLink();
                    monitorDataMapNodeLinkReq.setMonitorId(monitorFlowDesignerEntity.getMonitorId());
                    monitorDataMapNodeLinkReq.setFromMonitorNodeId(link.getFrom());
                    monitorDataMapNodeLinkReq.setToMonitorNodeId(link.getTo());
                    monitorDataMapNodeLinks.add(monitorDataMapNodeLinkReq);
                }
                boolean save = monitorMapNodeLinkService.saveBatch(monitorDataMapNodeLinks);
                if (!(remove && save)){
                    throw new DbRuntimeException("更新失败");
                }
                return 1;
            } catch (DbRuntimeException e) {
                status.setRollbackOnly();
                throw new RuntimeException(e);
            }
        });
    }

    @Override
    public List<GatherNodeExpressionVO> queryGatherNodeExpressionVO(String systemName, String className, String methodName) {
        List<MonitorDataMapNode> monitorDataMapNodes = monitorMapNodeService.lambdaQuery()
                .eq(MonitorDataMapNode::getGatherSystemName, systemName)
                .eq(MonitorDataMapNode::getGatherClazzName, className)
                .eq(MonitorDataMapNode::getGatherMethodName, methodName).list();
        if (ObjectUtil.isNull(monitorDataMapNodes)) return null;
        List<GatherNodeExpressionVO> gatherNodeExpressionVOS = new ArrayList<>();
        for (MonitorDataMapNode monitorDataMapNodeRes : monitorDataMapNodes) {
            // 2. 查询采集公式
            List<MonitorDataMapNodeField> monitorDataMapNodeFieldList = monitorMapNodeFieldService.lambdaQuery().eq(MonitorDataMapNodeField::getMonitorId, monitorDataMapNodeRes.getMonitorId())
                    .eq(MonitorDataMapNodeField::getMonitorNodeId, monitorDataMapNodeRes.getMonitorNodeId()).list();

            List<GatherNodeExpressionVO.Filed> fileds = new ArrayList<>();
            for (MonitorDataMapNodeField monitorDataMapNodeField : monitorDataMapNodeFieldList) {
                fileds.add(GatherNodeExpressionVO.Filed.builder()
                        .logName(monitorDataMapNodeField.getLogName())
                        .logIndex(monitorDataMapNodeField.getLogIndex())
                        .logType(monitorDataMapNodeField.getLogType())
                        .attributeField(monitorDataMapNodeField.getAttributeField())
                        .attributeName(monitorDataMapNodeField.getAttributeName())
                        .attributeOgnl(monitorDataMapNodeField.getAttributeOgnl())
                        .build());
            }

            gatherNodeExpressionVOS.add(GatherNodeExpressionVO.builder()
                    .monitorId(monitorDataMapNodeRes.getMonitorId())
                    .monitorNodeId(monitorDataMapNodeRes.getMonitorNodeId())
                    .gatherSystemName(monitorDataMapNodeRes.getGatherSystemName())
                    .gatherClazzName(monitorDataMapNodeRes.getGatherClazzName())
                    .gatherMethodName(monitorDataMapNodeRes.getGatherMethodName())
                    .fileds(fileds)
                    .build());
        }
        return gatherNodeExpressionVOS;
    }

    @Override
    public String queryMonitoryNameByMonitoryId(String monitorId) {
        return monitorMapService.lambdaQuery().eq(MonitorDataMap::getMonitorId, monitorId).one().getMonitorName();
    }

    @Override
    public void saveMonitoryData(MonitorDataEntity monitorDataEntity) {
        MonitorData monitorDataReq = new MonitorData();
        monitorDataReq.setMonitorId(monitorDataEntity.getMonitorId());
        monitorDataReq.setMonitorName(monitorDataEntity.getMonitorName());
        monitorDataReq.setMonitorNodeId(monitorDataEntity.getMonitorNodeId());
        monitorDataReq.setSystemName(monitorDataEntity.getSystemName());
        monitorDataReq.setClazzName(monitorDataEntity.getClazzName());
        monitorDataReq.setMethodName(monitorDataEntity.getMethodName());
        monitorDataReq.setAttributeName(monitorDataEntity.getAttributeName());
        monitorDataReq.setAttributeField(monitorDataEntity.getAttributeField());
        monitorDataReq.setAttributeValue(monitorDataEntity.getAttributeValue());
        monitorService.save(monitorDataReq);

        String cacheKey = RedissonConstant.MONITOR_NODE_DATA_COUNT_KEY + monitorDataEntity.getMonitorId() + RedissonConstant.UNDERLINE + monitorDataEntity.getMonitorNodeId();
        redisService.incr(cacheKey);
    }
}
